package com.guslegend.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.guslegend.enums.BizCodeEnum;
import com.guslegend.enums.CouponCategoryEnum;
import com.guslegend.enums.CouponPublishEnum;
import com.guslegend.enums.CouponUseState;
import com.guslegend.exception.BizException;
import com.guslegend.interceptor.LoginInterceptor;
import com.guslegend.mapper.CouponMapper;
import com.guslegend.mapper.CouponRecordMapper;
import com.guslegend.model.CouponDO;
import com.guslegend.model.CouponRecordDO;
import com.guslegend.model.LoginUser;
import com.guslegend.service.CouponService;
import com.guslegend.util.CommonUtil;
import com.guslegend.util.JsonData;
import com.guslegend.utils.CouponUtil;
import com.guslegend.vo.CouponVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author: guslegend
 * @description:
 * @create: 2025-05-30 08:05
 **/
@Service
@Slf4j
public class CouponServiceImpl implements CouponService {
    @Autowired
    private CouponMapper couponMapper;
    @Autowired
    private CouponRecordMapper couponRecordMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    /**
     * lua脚本 解锁  redis官网复制脚本即可
     */
    private String deleteScript = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

    @Override
    public Map<String, Object> pageCouponActivity(int page, int size) {
        Page<CouponDO> pageInfo = new Page<>(page, size);
        Page<CouponDO> selectPage = couponMapper.selectPage(pageInfo, new QueryWrapper<CouponDO>().eq("publish", CouponPublishEnum.PUBLISH).eq("category", CouponCategoryEnum.PROMOTION));
        HashMap<String, Object> map = new HashMap<>(3);
        map.put("total_page", selectPage.getPages());
        map.put("total_record", selectPage.getTotal());
        map.put("current_data", selectPage.getRecords().stream().map(couponDo -> CouponUtil.ObjParse(couponDo)).collect(Collectors.toList()));
        return map;
    }

    /**
     * * 1.查询优惠券是否存在
     * * 2.检验优惠券是否可以领取 库存 时间
     * * 3.扣减库存
     * * 4.保存优惠券领取记录
     * @param couponId       优惠券id
     * @param couponCategory 优惠券分类
     * @return
     */
    /**
     * 原生redisTemplate加锁  lua脚本解锁
     * @Override public JsonData addCoupon(long couponId, CouponCategoryEnum couponCategory) {
     * //uuid标记每一个锁 删除锁之前判断是不是自己的锁
     * String uuid = CommonUtil.getUUID();
     * //把优惠券的锁粒度下放到具体的优惠券 解决单人超领优惠券
     * String couponLockKey = "lock:coupon:" + couponId;
     * //加锁
     * if (redisTemplate.opsForValue().setIfAbsent(couponLockKey, uuid, Duration.ofSeconds(3000L))) {
     *
     * //业务逻辑
     * try {
     * LoginUser loginUser = LoginInterceptor.threadLocal.get();
     * //查询优惠券是否存在
     * CouponDO couponDO = couponMapper.selectOne(new QueryWrapper<CouponDO>().eq("id", couponId).eq("category", couponCategory.name()).eq("publish", CouponPublishEnum.PUBLISH));
     * //检验优惠券是否可以领取
     * CouponUtil.checkCoupon(couponDO, loginUser.getId(), couponRecordMapper);
     * //构建领券记录
     * CouponRecordDO couponRecordDO = new CouponRecordDO();
     * BeanUtils.copyProperties(couponDO, couponRecordDO);
     * couponRecordDO.setUseState(CouponUseState.NEW.name());
     * couponRecordDO.setCouponId(couponId);
     * couponRecordDO.setUserId(loginUser.getId());
     * couponRecordDO.setUserName(loginUser.getName());
     * couponRecordDO.setId(null);
     * //扣减库存
     * int rows = couponMapper.reduceStock(couponId);
     * if (rows == 1) {
     * //库存扣减成功 再插入记录
     * int insertRows = couponRecordMapper.insert(couponRecordDO);
     * return JsonData.buildSuccess(insertRows);
     * } else {
     * log.warn("优惠券发放失败：{},用户：{}", couponDO, loginUser);
     * return JsonData.buildResult(BizCodeEnum.COUPON_NO_STOCK);
     * }
     *
     * } finally {
     * //解锁 lua脚本原子性操作
     * boolean executeFlag = redisTemplate.execute(new DefaultRedisScript<>(deleteScript, Boolean.class), Arrays.asList(couponLockKey), uuid);
     * log.info("{} 解锁是否成功 ：{}", couponId + "_" + uuid, executeFlag);
     * }
     * } else {
     * try {
     * TimeUnit.MILLISECONDS.sleep(100);
     * } catch (InterruptedException e) {
     * log.error("等待优惠券锁失败");
     * }
     * //自旋
     * addCoupon(couponId, couponCategory);
     * }
     * return JsonData.buildSuccess();
     * }
     */
    @Override
    public JsonData addCoupon(long couponId, CouponCategoryEnum couponCategory) {

        //把优惠券的锁粒度下放到具体的优惠券 解决单人超领优惠券
        String couponLockKey = "lock:coupon:" + couponId;
        //加锁
        RLock lock = redissonClient.getLock(couponLockKey);
        lock.lock(10, TimeUnit.SECONDS);
        log.info("优惠券加锁成功 ：{}", Thread.currentThread().getId());

        //业务逻辑
        try {
            LoginUser loginUser = LoginInterceptor.threadLocal.get();
            //查询优惠券是否存在
            CouponDO couponDO = couponMapper.selectOne(new QueryWrapper<CouponDO>().eq("id", couponId).eq("category", couponCategory.name()).eq("publish", CouponPublishEnum.PUBLISH));
            //检验优惠券是否可以领取
            CouponUtil.checkCoupon(couponDO, loginUser.getId(), couponRecordMapper);
            //构建领券记录
            CouponRecordDO couponRecordDO = new CouponRecordDO();
            BeanUtils.copyProperties(couponDO, couponRecordDO);
            couponRecordDO.setUseState(CouponUseState.NEW.name());
            couponRecordDO.setCouponId(couponId);
            couponRecordDO.setUserId(loginUser.getId());
            couponRecordDO.setUserName(loginUser.getName());
            couponRecordDO.setId(null);
            //扣减库存
            int rows = couponMapper.reduceStock(couponId);
            if (rows == 1) {
                //库存扣减成功 再插入记录
                int insertRows = couponRecordMapper.insert(couponRecordDO);
                return JsonData.buildSuccess(insertRows);
            } else {
                log.warn("优惠券发放失败：{},用户：{}", couponDO, loginUser);
                return JsonData.buildResult(BizCodeEnum.COUPON_NO_STOCK);
            }

        } finally {
            //解锁
            lock.unlock();
            log.info("优惠券解锁成功 ：{}", Thread.currentThread().getId());
        }


    }

}